package com.yhn.registration.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hospital.common.base.Result;
import com.hospital.common.base.ThreadAttributes;
import com.hospital.common.dto.payment.module.OrderCreateDto;
import com.hospital.common.dto.registration.CreateRegistrationDto;
import com.hospital.common.enums.payment.module.OrderType;
import com.hospital.common.enums.registration.module.RegistrationStatus;
import com.hospital.common.pojo.TRegistration;
import com.hospital.common.pojo.TUser;
import com.hospital.common.utils.ThreadLocalUtils;
import com.yhn.registration.mapper.RegistrationMapper;
import com.yhn.registration.remote.PaymentFeignClient;
import com.yhn.registration.service.RegistrationService;
import com.yhn.registration.service.UserService;
import com.yhn.registration.vo.RegistrationVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

// import java.time.LocalDateTime;

/**
 * 挂号预约服务实现类
 *
 * @author yhn
 * @version 1.0
 */
@Service
public class RegistrationServiceImpl implements RegistrationService {

    @Autowired
    private RegistrationMapper registrationMapper;
    @Autowired
    private PaymentFeignClient paymentFeignClient;
    @Autowired
    private UserService userService;
    @Override
    @Transactional
    public Result<TRegistration> createRegistration(CreateRegistrationDto dto) {
        try {
            // 时间有效性校验：只能预约当前时间之后，且结束时间晚于开始时间
            if (dto.getAppointmentBookingStartTime() == null || dto.getAppointmentBookingEndTime() == null) {
                return Result.fail("预约时间不能为空");
            }
            java.time.LocalDateTime now = java.time.LocalDateTime.now();
            if (!dto.getAppointmentBookingStartTime().isAfter(now)) {
                return Result.fail("预约开始时间必须晚于当前时间");
            }
            if (!dto.getAppointmentBookingEndTime().isAfter(dto.getAppointmentBookingStartTime())) {
                return Result.fail("预约结束时间必须晚于开始时间");
            }
            // 调试信息
            System.out.println("=== 预约调试信息 ===");
            System.out.println("医生ID: " + dto.getDoctorId());
            System.out.println("预约开始时间: " + dto.getAppointmentBookingStartTime());
            System.out.println("预约结束时间: " + dto.getAppointmentBookingEndTime());

            // 1. 检查医生是否在指定日期有排班
            Integer scheduleExists = registrationMapper.checkDoctorScheduleExists(
                    dto.getDoctorId(), dto.getAppointmentBookingStartTime());

            System.out.println("排班检查结果: " + scheduleExists);

            if (scheduleExists == null || scheduleExists <= 0) {
                return Result.fail("该医生在此时间段没有排班");
            }

            // 2. 获取该时间段的最大患者数
            Integer maxPatients = registrationMapper.getMaxPatientsForTimeSlot(
                    dto.getDoctorId(), dto.getAppointmentBookingStartTime(), dto.getAppointmentBookingEndTime());

            System.out.println("最大患者数: " + maxPatients);

            if (maxPatients == null || maxPatients <= 0) {
                return Result.fail("该时间段没有排班信息");
            }

            // 3. 获取医生排班的当前已预约人数（快速检查）
            Integer currentBookedCount = registrationMapper.getCurrentBookedCount(
                    dto.getDoctorId(), dto.getAppointmentBookingStartTime());

            System.out.println("医生排班当前已预约人数: " + currentBookedCount);

            if (currentBookedCount == null) {
                return Result.fail("该时间段没有排班信息");
            }

            // 4. 检查是否有剩余号源（基于排班表）
            System.out.println("号源检查(排班表): currentBooked=" + currentBookedCount + ", maxPatients=" + maxPatients + ", 剩余=" + (maxPatients - currentBookedCount));

            if (currentBookedCount >= maxPatients) {
                return Result.fail("该时间段号源已满，请选择其他时间段");
            }

            // 5. 获取已预约人数（带锁，防止并发）- 精确验证
            Integer bookedCount = registrationMapper.getBookedCountForTimeSlot(
                    dto.getDoctorId(), dto.getAppointmentBookingStartTime(), dto.getAppointmentBookingEndTime());

            System.out.println("预约表实际已预约人数: " + bookedCount);

            if (bookedCount == null) {
                bookedCount = 0;
            }

            // 6. 数据一致性检查
            System.out.println("数据一致性检查: 排班表=" + currentBookedCount + ", 预约表=" + bookedCount);

            // 如果预约表的数量超过排班表，说明数据不一致，以预约表为准
            if (bookedCount >= maxPatients) {
                return Result.fail("该时间段号源已满，请选择其他时间段");
            }

            // 7. 检查患者当天同一科室预约限制（允许不同科室）
            int deptDailyLimitCount = registrationMapper.checkPatientDeptDailyLimit(
                    dto.getPatientId(), dto.getDepartmentId(), dto.getAppointmentBookingStartTime());
            if (deptDailyLimitCount > 0) {
                return Result.fail("当天同一科室只能预约一次");
            }

            // 8. 原子性更新医生排班的已预约人数（+1）
            int updateResult = registrationMapper.incrementCurrentBooked(
                    dto.getDoctorId(), dto.getAppointmentBookingStartTime());

            System.out.println("更新已预约人数结果: " + updateResult);

            if (updateResult == 0) {
                return Result.fail("更新排班信息失败，请稍后重试");
            }

            // 创建挂号记录
            TRegistration registration = new TRegistration();
            registration.setPatientId(dto.getPatientId());
            registration.setPatientName(dto.getPatientName());
            registration.setDoctorId(dto.getDoctorId());
            registration.setDoctorName(dto.getDoctorName());
            registration.setDepartmentId(dto.getDepartmentId());
            registration.setDepartment(dto.getDepartment());
            // registration_time字段暂时不设置，等支付时才设置
            registration.setRegistrationTime(null);
            registration.setAppointmentBookingStartTime(dto.getAppointmentBookingStartTime());
            registration.setAppointmentBookingEndTime(dto.getAppointmentBookingEndTime());
            registration.setState(RegistrationStatus.IN_PROGRESS.value);
            registration.setRegistrationFee(dto.getRegistrationFee());

            // 使用 MyBatis-Plus 插入
            registrationMapper.insert(registration);

            // 创建支付订单（订单类型：挂号 = 0）
            System.out.println("=== 开始创建支付订单 ===");
            OrderCreateDto orderCreateDto = new OrderCreateDto();
            // 优先使用线程上下文中的用户ID，避免支付侧查无此用户
            ThreadAttributes ta = ThreadLocalUtils.get();
            Long patientIdForOrder = registration.getPatientId();
            if(ta != null && ta.getUserId() != null){
                patientIdForOrder = ta.getUserId();
            }
            orderCreateDto.setPatientId(patientIdForOrder);
            orderCreateDto.setOrderType(OrderType.REGISTRATION.value);
            // 仅传递支付侧必需字段，避免在对方 convertValue 成 TRegistration 时解析 LocalDateTime
            Map<String, Object> businessData = new HashMap<>();
            businessData.put("id", registration.getId());
            businessData.put("registrationFee", registration.getRegistrationFee());
            orderCreateDto.setBusinessData(businessData);

            System.out.println("支付订单信息: " + orderCreateDto);
            System.out.println("患者ID: " + patientIdForOrder);
            System.out.println("订单类型: " + OrderType.REGISTRATION.value);
            System.out.println("业务数据: " + businessData);

            try {
                System.out.println("正在调用支付服务...");
                Result<?> paymentResult = paymentFeignClient.create(orderCreateDto);
                System.out.println("支付服务返回结果: " + paymentResult);
            } catch (Exception ex) {
                System.err.println("创建支付订单失败: " + ex.getMessage());
                ex.printStackTrace();
            }

            return Result.success(registration);

        } catch (Exception e) {
            // 系统异常，记录日志
            System.err.println("创建预约失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("系统异常，请稍后重试");
        }
    }

    @Override
    public Result<java.util.List<TRegistration>> listByPatientId(Long patientId) {
        if (patientId == null) {
            return Result.fail("患者ID不能为空");
        }
        java.util.List<TRegistration> list = registrationMapper.queryByPatientId(patientId);
        return Result.success(list);
    }

    @Override
    public Result<Integer> getDoctorDailyAppointments() {
        ThreadAttributes ta = ThreadLocalUtils.get();
        Long doctorId = (ta != null) ? ta.getUserId() : null;
        if (doctorId == null) {
            return Result.fail("请先登录医生账号");
        }
        try {
            Integer count = registrationMapper.getDoctorDailyAppointments(doctorId);
            return Result.success(count != null ? count : 0);
        } catch (Exception e) {
            System.err.println("查询医生当天预约人数失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("查询失败，请稍后重试");
        }
    }

    @Override
    public Result<Integer> countDoctorActiveRegistrations() {
        ThreadAttributes ta = ThreadLocalUtils.get();
        Long doctorId = (ta != null) ? ta.getUserId() : null;
        if (doctorId == null) {
            return Result.fail("请先登录医生账号");
        }
        try {
            Integer count = registrationMapper.countDoctorActiveRegistrations(doctorId);
            return Result.success(count == null ? 0 : count);
        } catch (Exception e) {
            System.err.println("统计医生进行中与已完成数量失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("统计失败，请稍后重试");
        }
    }

    @Override
    public Result<java.util.Map<String, Integer>> countDoctorRegistrationsByState() {
        ThreadAttributes threadAttributes = ThreadLocalUtils.get();
        Long doctorId = (threadAttributes != null) ? threadAttributes.getUserId() : null;
        if (doctorId == null) {
            return Result.fail("请先登录医生账号");
        }
        try {
            java.util.Map<String, Integer> map = registrationMapper.countDoctorRegistrationsByState(doctorId);
            if (map == null) {
                map = new java.util.HashMap<>();
                map.put("inProgress", 0);
                map.put("completed", 0);
                map.put("cancelled", 0);
                map.put("notVisited", 0);
            } else {
                map.putIfAbsent("inProgress", 0);
                map.putIfAbsent("completed", 0);
                map.putIfAbsent("cancelled", 0);
                map.putIfAbsent("notVisited", 0);
            }
            return Result.success(map);
        } catch (Exception e) {
            System.err.println("统计医生各状态数量失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("统计失败，请稍后重试");
        }
    }

    @Override
    public Result<java.util.Map<String, Integer>> countPatientRegistrationsByState() {
        ThreadAttributes ta = ThreadLocalUtils.get();
        Long patientId = (ta != null) ? ta.getUserId() : null;
        if (patientId == null) {
            return Result.fail("请先登录患者账号");
        }
        try {
            java.util.Map<String, Integer> map = registrationMapper.countPatientRegistrationsByState(patientId);
            if (map == null) {
                map = new java.util.HashMap<>();
            }
            map.putIfAbsent("inProgress", 0);
            map.putIfAbsent("completed", 0);
            map.putIfAbsent("cancelled", 0);
            map.putIfAbsent("notVisited", 0);
            return Result.success(map);
        } catch (Exception e) {
            System.err.println("统计患者各状态数量失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("统计失败，请稍后重试");
        }
    }

    @Override
    @Transactional
    public Result<String> cancelRegistration(Long registrationId) {
        if (registrationId == null) {
            return Result.fail("预约ID不能为空");
        }
        try {
            // 查询预约记录
            TRegistration registration = registrationMapper.selectById(registrationId);
            if (registration == null) {
                return Result.fail("预约记录不存在");
            }

            // 减少排班表的已预约人数
            int updateResult = registrationMapper.decrementCurrentBooked(
                    registration.getDoctorId(),
                    registration.getAppointmentBookingStartTime()
            );
            if (updateResult <= 0) {
                System.err.println("更新排班表失败，预约ID: " + registrationId);
            }

            // 更新预约状态为已取消
            registration.setState(2);
            registrationMapper.updateById(registration);

            return Result.success("预约取消成功");
        } catch (Exception e) {
            System.err.println("取消预约失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("取消预约失败，请稍后重试");
        }
    }

    @Override
    @Transactional
    public Result<String> syncScheduleData() {
        try {
            // 1. 重置所有排班表的已预约人数为0
            int resetResult = registrationMapper.resetAllCurrentBooked();
            // 2. 根据实际预约记录重新计算已预约人数
            int updateResult = registrationMapper.recalculateCurrentBooked();
            return Result.success("数据同步完成，重置了 " + resetResult + " 条排班记录，更新了 " + updateResult + " 条记录");
        } catch (Exception e) {
            System.err.println("同步排班表数据失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("数据同步失败，请稍后重试");
        }
    }

    @Override
    @Transactional
    public Result<String> cancelAndRestore(Long patientId, Long registrationId) {
        if (patientId == null || registrationId == null) {
            return Result.fail("患者ID和预约ID不能为空");
        }
        try {
            // 1) 调用支付模块取消挂号
            try {
                paymentFeignClient.cancelRegistration(patientId, registrationId);
            } catch (Exception ex) {
                System.err.println("调用支付取消失败: " + ex.getMessage());
            }

            // 2) 本地回滚号源与状态
            TRegistration registration = registrationMapper.selectById(registrationId);
            if (registration == null) {
                return Result.fail("预约记录不存在");
            }

            // 回滚排班人数
            registrationMapper.decrementCurrentBooked(
                    registration.getDoctorId(),
                    registration.getAppointmentBookingStartTime()
            );

            // 更新状态
            registration.setState(2);
            registrationMapper.updateById(registration);

            return Result.success("取消成功，号源已回滚");
        } catch (Exception e) {
            System.err.println("取消并回滚失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("取消失败，请稍后重试");
        }
    }

    @Override
    public Result<com.hospital.common.base.PageResult<TRegistration>> listPatientRegistrationsByPage(Integer state, Integer pageNum, Integer pageSize) {
        ThreadAttributes ta = ThreadLocalUtils.get();
        Long patientId = ta.getUserId();
        try {
            // 参数校验
            if (patientId == null) {
                return Result.fail("患者ID不能为空");
            }
            if (pageNum == null || pageNum < 1) {
                pageNum = 1;
            }
            if (pageSize == null || pageSize < 1) {
                pageSize = 10;
            }
            if (pageSize > 100) {
                pageSize = 100; // 限制最大页面大小
            }

            // 计算偏移量
            int offset = (pageNum - 1) * pageSize;

            // 查询总数
            Integer total = registrationMapper.countPatientRegistrations(patientId, state);
            if (total == null) {
                total = 0;
            }

            // 查询分页数据
            List<TRegistration> records = registrationMapper.queryPatientRegistrationsByPage(patientId, state, offset, pageSize);
            if (records == null) {
                records = new java.util.ArrayList<>();
            }

            // 构建分页结果
            com.hospital.common.base.PageResult<TRegistration> pageResult = new com.hospital.common.base.PageResult<>();
            pageResult.setTotal((long) total);
            pageResult.setRows(records);

            return Result.success(pageResult);
        } catch (Exception e) {
            System.err.println("分页查询患者预约记录失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("查询失败，请稍后重试");
        }
    }

    @Override
    public Result<com.hospital.common.base.PageResult<TRegistration>> listAllRegistrationsByPage(String patientName, String doctorName, String department, Integer state, String startTime, String endTime, Integer pageNum, Integer pageSize) {
        try {
            // 参数校验
            if (pageNum == null || pageNum < 1) {
                pageNum = 1;
            }
            if (pageSize == null || pageSize < 1) {
                pageSize = 10;
            }
            if (pageSize > 100) {
                pageSize = 100; // 限制最大页面大小
            }

            // 计算偏移量
            int offset = (pageNum - 1) * pageSize;

            // 查询总数
            Integer total = registrationMapper.countAllRegistrations(patientName, doctorName, department, state, startTime, endTime);
            if (total == null) {
                total = 0;
            }

            // 查询分页数据
            List<TRegistration> records = registrationMapper.queryAllRegistrationsByPage(patientName, doctorName, department, state, startTime, endTime, offset, pageSize);
            if (records == null) {
                records = new java.util.ArrayList<>();
            }

            // 构建分页结果
            com.hospital.common.base.PageResult<TRegistration> pageResult = new com.hospital.common.base.PageResult<>();
            pageResult.setTotal((long) total);
            pageResult.setRows(records);

            return Result.success(pageResult);
        } catch (Exception e) {
            System.err.println("分页查询所有预约记录失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("查询失败，请稍后重试");
        }
    }

    @Override
    public Result<java.util.Map<String, Object>> getRegistrationStats() {
        try {
            java.util.Map<String, Object> stats = registrationMapper.getRegistrationStats();
            if (stats == null) {
                stats = new java.util.HashMap<>();
                stats.put("total", 0);
                stats.put("inProgress", 0);
                stats.put("completed", 0);
                stats.put("cancelled", 0);
                stats.put("notVisited", 0);
                stats.put("totalFee", 0.0);
            }
            return Result.success(stats);
        } catch (Exception e) {
            System.err.println("获取预约统计信息失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("获取统计信息失败，请稍后重试");
        }
    }

    @Override
    public Result<TRegistration> getRegistrationDetail(Long id) {
        try {
            if (id == null) {
                return Result.fail("预约记录ID不能为空");
            }

            TRegistration registration = registrationMapper.getRegistrationById(id);
            if (registration == null) {
                return Result.fail("预约记录不存在");
            }

            return Result.success(registration);
        } catch (Exception e) {
            System.err.println("获取预约详情失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("获取预约详情失败，请稍后重试");
        }
    }

    @Override
    public Result<Integer> countTodayAppointments() {
        try {
            Integer total = registrationMapper.countTodayAppointments();
            return Result.success(total == null ? 0 : total);
        } catch (Exception e) {
            System.err.println("统计当日预约总数失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("统计失败，请稍后重试");
        }
    }

    @Override
    public List<RegistrationVo> queryByDoctorId() {
        ThreadAttributes ta = ThreadLocalUtils.get();
        Long doctorId = ta.getUserId();
        if(doctorId==null){
            return null;
        }
        LambdaQueryWrapper<TRegistration> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ne(TRegistration::getState,RegistrationStatus.CANCELED.value)
                .eq(TRegistration::getDoctorId,doctorId);
        return registrationMapper.selectList(queryWrapper).stream().map(this::convert).toList();

    }

    @Override
    public Result<java.util.List<RegistrationVo>> doctorListTodayPatients() {
        ThreadAttributes ta = ThreadLocalUtils.get();
        Long doctorId = (ta != null) ? ta.getUserId() : null;
        if (doctorId == null) {
            return Result.fail("请先登录医生账号");
        }
        try {
            java.util.List<TRegistration> rows = registrationMapper.doctorListTodayPatients(doctorId);
            if (rows == null) rows = new java.util.ArrayList<>();
            java.util.List<RegistrationVo> voList = rows.stream().map(this::convert).toList();
            return Result.success(voList);
        } catch (Exception e) {
            System.err.println("查询医生今日患者失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("查询失败，请稍后重试");
        }
    }

    @Override
    public Result<com.hospital.common.base.PageResult<RegistrationVo>> doctorSearchPatients(String patientName,
                                                                                            String startTime,
                                                                                            String endTime,
                                                                                            Integer pageNum,
                                                                                            Integer pageSize) {
        ThreadAttributes ta = ThreadLocalUtils.get();
        Long doctorId = (ta != null) ? ta.getUserId() : null;
        if (doctorId == null) {
            return Result.fail("请先登录医生账号");
        }
        if (pageNum == null || pageNum < 1) pageNum = 1;
        if (pageSize == null || pageSize < 1) pageSize = 10;
        if (pageSize > 100) pageSize = 100;

        // 处理时间参数，如果为空字符串或"null"字符串，则设为null
        if (startTime == null || startTime.isBlank() || "null".equals(startTime)) {
            startTime = null;
        }
        if (endTime == null || endTime.isBlank() || "null".equals(endTime)) {
            java.time.LocalDate today = java.time.LocalDate.now();
            endTime = today.toString() + " 23:59:59";
        }

        // 处理患者姓名参数
        if (patientName == null || "null".equals(patientName)) {
            patientName = null;
        }

        int offset = (pageNum - 1) * pageSize;
        try {
            // 添加调试日志
            System.out.println("医生查询患者参数 - doctorId: " + doctorId + ", patientName: " + patientName +
                    ", startTime: " + startTime + ", endTime: " + endTime +
                    ", pageNum: " + pageNum + ", pageSize: " + pageSize);

            Integer total = registrationMapper.countDoctorSearchPatients(doctorId, patientName, startTime, endTime);
            if (total == null) total = 0;
            System.out.println("查询到的总记录数: " + total);

            java.util.List<TRegistration> rows = registrationMapper.doctorSearchPatients(doctorId, patientName, startTime, endTime, offset, pageSize);
            if (rows == null) rows = new java.util.ArrayList<>();
            System.out.println("查询到的记录数: " + rows.size());

            java.util.List<RegistrationVo> voList = rows.stream().map(this::convert).toList();
            com.hospital.common.base.PageResult<RegistrationVo> page = new com.hospital.common.base.PageResult<>();
            page.setTotal(total.longValue());
            page.setRows(voList);
            return Result.success(page);
        } catch (Exception e) {
            System.err.println("医生条件查询患者失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("查询失败，请稍后重试");
        }
    }
    private RegistrationVo convert(TRegistration  registration){
        RegistrationVo vo = new RegistrationVo();
        vo.setId(registration.getId());
        TUser patient = userService.getById(registration.getPatientId());
        if(!ObjectUtils.isEmpty(patient)){
            vo.setPatientId(patient.getId());
            vo.setName(patient.getName());
            vo.setSex(patient.getSex());
            vo.setPhone(patient.getPhone());
        }
        vo.setAppointmentBookingStartTime(registration.getAppointmentBookingStartTime());
        vo.setState(registration.getState());
        return vo;
    }


    @Override
    @Transactional
    public Result<String> confirmVisited(Long registrationId) {
        if (registrationId == null) {
            return Result.fail("预约ID不能为空");
        }
        try {
            TRegistration registration = registrationMapper.selectById(registrationId);
            if (registration == null) {
                return Result.fail("预约记录不存在");
            }
            registration.setState(1); // 完成
            registrationMapper.updateById(registration);
            return Result.success("已标记为完成");
        } catch (Exception e) {
            System.err.println("标记完成失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("操作失败，请稍后重试");
        }
    }

    @Override
    @Transactional
    public Result<String> markNotVisited(Long registrationId) {
        if (registrationId == null) {
            return Result.fail("预约ID不能为空");
        }
        try {
            TRegistration registration = registrationMapper.selectById(registrationId);
            if (registration == null) {
                return Result.fail("预约记录不存在");
            }
            registration.setState(3); // 未就诊
            registrationMapper.updateById(registration);
            return Result.success("已标记为未就诊");
        } catch (Exception e) {
            System.err.println("标记未就诊失败: " + e.getMessage());
            e.printStackTrace();
            return Result.fail("操作失败，请稍后重试");
        }
    }
}
